package cn.jtool.dig.common.netty.hander;

import cn.jtool.dig.common.netty.CtxUtils;
import cn.jtool.dig.common.netty.supports.ArraysUtils;
import cn.jtool.dig.common.netty.supports.Constants;
import cn.jtool.dig.common.netty.supports.Convert;
import cn.jtool.dig.common.netty.supports.PacketProcessing;
import com.google.common.collect.Lists;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.util.AttributeKey;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.Objects;

@Slf4j
public class TunnelDecoder extends ByteToMessageDecoder {

    private static final AttributeKey<byte[]> SURPLUS_DATA = AttributeKey.newInstance("SURPLUS_DATA");

    private final boolean debug;

    private final String name;

    public TunnelDecoder(String name, boolean debug) {
        this.name = name;
        this.debug = debug;
    }

    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf in, List<Object> list) throws Exception {
        if(this.debug){
            log.info("{} 收到数据: {}", this.name,  ByteBufUtil.hexDump(in).toUpperCase());
        }
        if (in.writerIndex() > in.readerIndex()) {
            byte[] bytes = this.combin(channelHandlerContext, in);
            List<byte[]> result = this.spliter(bytes);
            for (byte[] packet : result) {
                packet = this.escape(packet);
                if (this.isComplete(packet)) {
                    boolean checkReuslt = this.check(packet);
                    CtxUtils.setAttribute(channelHandlerContext, SURPLUS_DATA, null);
                    if (checkReuslt) {
                        list.add(packet);
                    }
                } else {
                    CtxUtils.setAttribute(channelHandlerContext, SURPLUS_DATA, packet);
                }
            }
        }
    }

    private byte[] combin(ChannelHandlerContext channelHandlerContext, ByteBuf in){
        byte[] bytes = new byte[in.writerIndex() - in.readerIndex()];
        in.readBytes(bytes);
        byte[] surplusData = CtxUtils.getAttribute(channelHandlerContext, SURPLUS_DATA);
        if(Objects.nonNull(surplusData)){
            bytes = ArraysUtils.arraycopy(surplusData, bytes);
        }
        return bytes;
    }

    private List<byte[]> spliter(byte[] bytes){
        List<byte[]> result = Lists.newArrayList();
        int index = -1;
        for (int i = 0, length = bytes.length; i < length; i++) {
            byte[] temp;
            if (index == -1) {
                if (Constants.AGENT_PACKET_BEGIN == bytes[i]) {
                    index = i;
                }
            } else {
                if (Constants.AGENT_PACKET_END == bytes[i]) {
                    if (i - index + 1 < Constants.AGENT_MIN_PACKET) {
                        index = i;
                    } else {
                        temp = ArraysUtils.subarrays(bytes, index, i - index + 1);
                        result.add(temp);
                        index = -1;
                    }
                }
            }
        }
        if (index != -1) {
            result.add(ArraysUtils.subarrays(bytes, index, bytes.length - index));
        }
        return result;
    }

    private byte[] escape(byte[] bytes) {
        byte[] ret = PacketProcessing.unEscape(
                ArraysUtils.subarrays(bytes, 1, bytes.length - 2),
                Constants.AGENT_TO_ESCAPE,
                Constants.AGENT_ESCAPE
        );
        return ArraysUtils.concatAll(new byte[]{Constants.AGENT_PACKET_BEGIN}, ret, new byte[]{Constants.AGENT_PACKET_END});
    }

    private boolean check(byte[] sourceBytes) {
        byte checkByte = PacketProcessing.checkPackage(ArraysUtils.subarrays(sourceBytes, 1, sourceBytes.length - 3));
        if(checkByte != sourceBytes[sourceBytes.length - 2]){
            log.error("校验码错误, {}, {}, {}", checkByte, sourceBytes[sourceBytes.length - 2], Convert.bytesToHexString(sourceBytes));
            return false;
        }
        return true;
    }

    private boolean isComplete(byte[] packet) {
        return packet[0] == Constants.AGENT_PACKET_BEGIN && packet[packet.length - 1] == Constants.AGENT_PACKET_END;
    }
}
